home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #2 / Ham Radio 2000 - Volume 2.iso / HAMV2 / TCP_IP / TNOS230S / TIMER.C < prev    next >
Encoding:
C/C++ Source or Header  |  1997-09-14  |  9.1 KB  |  435 lines

  1. /* General purpose software timer facilities
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  */
  4. #include "global.h"
  5. #include "timer.h"
  6. #include "proc.h"
  7. #include "daemon.h"
  8. #include "hardware.h"
  9. #include "commands.h"
  10. #ifndef MSDOS
  11. #include "session.h"
  12. #include "socket.h"
  13. #endif
  14.  
  15. #if !defined(_lint)
  16. static char rcsid[] OPTIONAL = "$Id: timer.c,v 1.23 1997/09/14 14:37:46 root Exp root $";
  17. #endif
  18.  
  19.  
  20.  
  21. /* Head of running timer chain.
  22.  * The list of running timers is sorted in increasing order of expiration;
  23.  * i.e., the first timer to expire is always at the head of the list.
  24.  */
  25. struct timer *Timers;
  26. static int Timer_mutex = TNOS_MUTEX_UNLOCKED;
  27.  
  28. static void t_alarm (void *x);
  29. static int valid_timer (struct timer *t, int exitOnErr);
  30. void stopalltimers (struct proc *pp);
  31. static void start_timer_internal (struct timer *t, struct proc *p);
  32.  
  33.  
  34. struct timerstats {
  35.     uint32 passes;
  36.     uint32 started;
  37.     uint32 stopped;
  38.     uint32 expired;
  39.     uint32 alarms;
  40. };
  41.  
  42. static struct timerstats tstats = {0, 0, 0, 0, 0};
  43.  
  44.  
  45.  
  46. /* Process that handles clock ticks */
  47. /* Fixed to solve some timing problems when multiple ticks
  48.  * get handled at once... from Walt Corey, KZ1F
  49.  */
  50. void
  51. timerproc (
  52. int i OPTIONAL,
  53. void *v1 OPTIONAL,
  54. void *v2 OPTIONAL
  55. ) {
  56. register struct timer *t;
  57. char i_state;
  58. int tmp;
  59. void (**vf) (void);
  60. int32 cur_clock;
  61.  
  62.     server_disconnect_io ();
  63.     Timer_mutex = TNOS_MUTEX_UNLOCKED;
  64.     for ( ; ; ) {
  65.         kwait (NULL);    /* Let them run before handling more ticks */
  66.  
  67.         /* Atomic read and decrement of Tick */
  68.         for ( ; ; ) {
  69.             i_state = (char) disable ();
  70.             tmp = Tick;
  71.             if (tmp != 0) {
  72.                 Tick--;
  73.                 restore (i_state);
  74.                 break;
  75.             }
  76.             restore (i_state);
  77.             kwait (&Tick);
  78.         }
  79. #ifndef TNOS_68K
  80.         if (!istate ()) {
  81.             restore (1);
  82. #ifdef MSDOS
  83.             tcmdprintf ("timer: ints were off!\n");
  84. #endif
  85.         }
  86. #endif
  87.  
  88.         tstats.passes++;
  89.  
  90.         /* Call the functions listed in config.c */
  91.         for (vf = Cfunc; *vf != NULL; vf++)
  92.             (*vf) ();
  93.  
  94.         if (Timers == NULLTIMER)
  95.             continue;    /* No active timers, all done */
  96.  
  97.         cur_clock = rdclock();
  98.         kmutex_lock (&Timer_mutex);
  99.  
  100.         /* Now go through the list of expired timers, removing each
  101.          * one and kicking the notify function, if there is one
  102.          * Note use of subtraction and comparison to zero rather
  103.          * than the more obvious simple comparison; this avoids
  104.          * problems when the clock count wraps around.
  105.          */
  106.         while (Timers != NULLTIMER && (cur_clock - Timers->expiration) >= 0) {
  107.             if (Timers->next == Timers) {
  108.                 tcmdprintf ("PANIC: Timer loop at %lx\n", (long) Timers);
  109.                 iostop ();
  110.                 exit (1);
  111.             }
  112.             /* Save Timers since stop_timer will change it */
  113.             t = Timers;
  114.             Timers = t->next;
  115.  
  116.             t->state = TIMER_EXPIRE;
  117.             t->next = NULLTIMER;
  118.             tstats.expired++;
  119.             if (valid_timer (t, 1) == 1)    {
  120.                 if (t->func)
  121.                     (*t->func) (t->arg);
  122.             }
  123.         }
  124.         kmutex_unlock (&Timer_mutex);
  125.     }
  126. }
  127.  
  128.  
  129.  
  130. /* Start a timer - link to specific process */
  131. static void
  132. start_timer_internal (struct timer *t, struct proc *p)
  133. {
  134. register struct timer *tnext;
  135. struct timer *tprev = NULLTIMER;
  136.  
  137.     if (t == NULLTIMER)
  138.         return;
  139.  
  140.     if (t->next != NULLTIMER)
  141.         stop_timer (t);
  142.  
  143.     if (t->duration == 0)
  144.         return;        /* A duration value of 0 disables the timer */
  145.  
  146.     tstats.started++;
  147.     t->next = NULLTIMER;    /* insure forward chain is NULL */
  148.     t->expiration = rdclock() + t->duration;
  149.     t->state = TIMER_RUN;
  150.  
  151.     t->magic1 = TIMER_MAGIC1;
  152.     t->magic2 = TIMER_MAGIC2;
  153.     t->theproc = (void *) p;
  154.  
  155.     /* Find right place on list for this guy. Once again, note use
  156.      * of subtraction and comparison with zero rather than direct
  157.      * comparison of expiration times.
  158.      */
  159.     for (tnext = Timers; valid_timer(tnext, 1) == 1; tprev = tnext, tnext = tnext->next) {
  160.         if ((tnext->expiration - t->expiration) >= 0)
  161.             break;
  162.     }
  163.  
  164.     if (tnext == t)
  165.         tnext = NULLTIMER;
  166.  
  167.     /* At this point, tprev points to the entry that should go right
  168.      * before us, and tnext points to the entry just after us. Either or
  169.      * both may be null.
  170.      */
  171.     if (tprev == NULLTIMER)
  172.         Timers = t;    /* Put at beginning */
  173.     else
  174.         tprev->next = t;
  175.  
  176.     t->next = tnext;
  177. }
  178.  
  179.  
  180.  
  181. /* Start a timer */
  182. void
  183. start_timer (struct timer *t)
  184. {
  185.     start_timer_internal (t, Curproc);
  186. }
  187.  
  188.  
  189. /* Start a timer, detached from the current process */
  190. void
  191. start_detached_timer (struct timer *timer)
  192. {
  193.     start_timer_internal (timer, Command->proc);
  194. }
  195.  
  196.  
  197.  
  198. /* Stop a timer */
  199. void
  200. stop_timer (struct timer *timer)
  201. {
  202. register struct timer *t;
  203. struct timer *tlast = NULLTIMER;
  204.  
  205.     if (!valid_timer(timer, 0) || !run_timer (timer))
  206.         return;
  207.  
  208.     /* Verify that timer is really on list */
  209.     for (t = Timers; valid_timer (t, 1) == 1; tlast = t, t = t->next)
  210.         if (t == timer)
  211.             break;
  212.  
  213.     if (t != NULLTIMER)    {
  214.         tstats.stopped++;
  215.  
  216.         /* Delete from active timer list */
  217.         if (tlast != NULLTIMER)
  218.             tlast->next = t->next;
  219.         else
  220.             Timers = t->next;    /* Was first on list */
  221.  
  222.         t->state = TIMER_STOP;
  223.         t->next = NULLTIMER;
  224.     }
  225. }
  226.  
  227.  
  228.  
  229. /* called from killproc to cleanup pending timers */
  230. void
  231. stopalltimers (struct proc *pp)
  232. {
  233. struct timer *t, *tmp;
  234.  
  235.     for (t = Timers; valid_timer (t, 0) == 1; t = tmp)    {
  236.         tmp = t->next;
  237.         if (t->theproc == pp)
  238.             stop_timer (t);
  239.     }
  240. }
  241.  
  242.  
  243.  
  244. /* Return milliseconds remaining on this timer */
  245. int32
  246. read_timer (struct timer *t)
  247. {
  248. int32 remaining;
  249.  
  250.     if (t == NULLTIMER || !run_timer (t))
  251.         return 0;
  252.     remaining = t->expiration - rdclock();
  253.     if (remaining <= 0)
  254.         return 0;    /* Already expired */
  255.     else
  256.         return remaining * MSPTICK;
  257. }
  258.  
  259.  
  260.  
  261. void
  262. set_timer (struct timer *t, int32 interval)
  263. {
  264. #ifdef MSDOS
  265. #define FUDGE 1
  266. #else
  267. #define FUDGE 0
  268. #endif
  269.  
  270.     if (t == NULLTIMER)
  271.         return;
  272.  
  273.     /* Round the interval up to the next full tick, and then
  274.      * add another tick to guarantee that the timeout will not
  275.      * occur before the interval is up. This is necessary because
  276.      * we're asynchonous with the system clock.
  277.      */
  278.     if (interval != 0)
  279.         t->duration = FUDGE + (interval + MSPTICK - 1) / MSPTICK;
  280.     else
  281.         t->duration = 0;
  282. }
  283.  
  284.  
  285.  
  286. /* Delay process for specified number of milliseconds.
  287.  * Normally returns 0; returns -1 if aborted by alarm.
  288.  */
  289. int
  290. kpause (int32 ms)
  291. {
  292. int val = 0;
  293.  
  294.     if (Curproc == NULLPROC || ms == 0)
  295.         return 0;
  296.     kalarm (ms);
  297.     /* The actual event doesn't matter, since we'll be alerted */
  298.     while (Curproc->alarm.state == TIMER_RUN) {
  299.         if ((val = kwait (Curproc)) != 0)
  300.             break;
  301.     }
  302.     kalarm (0L);        /* Make sure it's stopped, in case we were killed */
  303.     return (val == EALARM) ? 0 : -1;
  304. }
  305.  
  306.  
  307.  
  308. static void
  309. t_alarm (void *x)
  310. {
  311.     tstats.alarms++;
  312.     alert ((struct proc *) x, EALARM);
  313. }
  314.  
  315.  
  316.  
  317. /* Send signal to current process after specified number of milliseconds */
  318. void
  319. kalarm (int32 ms)
  320. {
  321.     if (Curproc != NULLPROC) {
  322.         set_timer (&Curproc->alarm, ms);
  323.         Curproc->alarm.func = t_alarm;
  324.         Curproc->alarm.arg = (char *) Curproc;
  325.         start_timer (&Curproc->alarm);
  326.     }
  327. }
  328.  
  329.  
  330.  
  331. /* Convert time count in seconds to printable days:hr:min:sec format */
  332. char *
  333. tformat (int32 t)
  334. {
  335. static char buf[17];
  336. int days, hrs, mins, secs;
  337. int minus = 0;
  338.  
  339.     if (t < 0) {
  340.         t = -t;
  341.         minus = 1;
  342.     }
  343.  
  344.     secs = t % 60;
  345.     t /= 60;
  346.     mins = t % 60;
  347.     t /= 60;
  348.     hrs = t % 24;
  349.     days = t / 24;
  350.  
  351.     sprintf (buf, "%s%d:%02d:%02d:%02d", (minus) ? "-" : "", days, hrs, mins, secs);
  352.     return buf;
  353. }
  354.  
  355.  
  356.  
  357. /* Read the Clock global variable, with interrupts off to avoid possible
  358.  * inconsistency on 16-bit machines
  359.  */
  360. int32
  361. rdclock(void)
  362. {
  363. int i_state;
  364. int32 rval;
  365.  
  366.     i_state = disable ();
  367.     rval = Clock;
  368.     restore (i_state);
  369.     return rval;
  370. }
  371.  
  372.  
  373.  
  374. int
  375. valid_timer (struct timer *t, int exitOnErr)
  376. {
  377. struct proc *theps;
  378.  
  379.     if (t == NULLTIMER)
  380.         return 0;
  381.     if (t->magic1 != TIMER_MAGIC1 || t->magic2 != TIMER_MAGIC2)    {
  382.         if (exitOnErr && shall_we_crash())
  383.             crash_it_already ("corrupted timer table 1");
  384.         return -1;
  385.     }
  386.  
  387.     theps = (struct proc *) t->theproc;
  388.     if (theps == NULLPROC || theps->name == NULLCHAR)    {
  389.         if (exitOnErr && shall_we_crash())
  390.             crash_it_already ("corrupted timer table 2");
  391.         return -1;
  392.     }        
  393.     return 1;
  394. }
  395.  
  396.  
  397.  
  398. /* Display the contents of the timer queue in human-readable format */
  399.  
  400. int
  401. dotimers (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
  402. {
  403. struct timer *t;
  404. int sentheader = 0;
  405. struct proc *theps;
  406. int32 exp;
  407.  
  408.     tprintf ("\nTimer Stats:\n    passes %lu started %lu stopped %lu\n    expired %lu alarms %lu\n",
  409.         tstats.passes, tstats.started, tstats.stopped, tstats.expired, tstats.alarms);
  410.  
  411.  
  412.     /* in order to make sure that this routine and timerproc are NOT
  413.        fighting for the timer queue, we put a mutex around it. */
  414.     kmutex_lock (&Timer_mutex);
  415.     for (t = Timers; t != NULLTIMER; t = t->next)    {
  416.         theps = (struct proc *) t->theproc;
  417.         if (!valid_timer (t, 1))    {
  418.             stop_timer (t);
  419.             break;
  420.         }
  421.         if (!sentheader)    {
  422.             sentheader++;
  423.             tprintf ("\n%-42s    %-10s    %s\n", "Process", "  Duration", "  Expires At");
  424.             tprintf (  "%-42s    %-10s    %s\n", "=======", "  ========", "==============");
  425.         }
  426.         exp = t->expiration * MSPTICK;
  427.         tprintf ("%-42.42s    %10ld    %s.%03ld\n", theps->name, dur_timer(t), 
  428.             tformat (exp / 1000), exp % 1000);
  429.     }
  430.     kmutex_unlock (&Timer_mutex);
  431.     tputc ('\n');
  432.     return 0;
  433. }
  434.  
  435.